#I Import and Initialize
import  os, pygame
from    data import *
import  math

TOP_SIDE        = 0
BOTTOM_SIDE     = 2
LEFT_SIDE       = 3
RIGHT_SIDE      = 1
SCREEN_WIDTH    = 800
SCREEN_HEIGHT   = 480



#############################################################################
# Calculates the horizontal speed based on the given deltas.
#############################################################################
def speed_to_side(dx,dy):
    if abs(dx) > abs(dy):
        dy = 0
    else:
        dx = 0
    if dy < 0:
        return 0
    elif dx > 0:
        return 1
    elif dy > 0:
        return 2
    elif dx < 0:
        return 3
    else:
        return 0, 0



#############################################################################
# Represents a collidable object.
#############################################################################
class Collidable(pygame.sprite.Sprite):
    


    #############################################################################
    # Initializes a new instance of Collidable.
    #############################################################################
    def __init__(self, *groups):
        pygame.sprite.Sprite.__init__(self, groups)
        
        self.collision_groups = []
        self.xoffset = 0
        self.yoffset = 0
    


    #############################################################################
    # Adds a group collision.
    #############################################################################
    def collide(self, group):
        if group not in self.collision_groups:
            self.collision_groups.append(group)
    


    #############################################################################
    # Moves this object's rect.
    #############################################################################
    def move(self, dx, dy, collide=True):
        if collide:
            if dx!=0:
                dx, dummy = self.__move(dx,0)
            if dy!=0:
                dummy, dy = self.__move(0,dy)
        else:
            self.rect.move_ip(dx, dy)
        return dx, dy
    


    #############################################################################
    # Clamps the object into position.
    #############################################################################
    def clamp_off(self, sprite, side):
        if side == TOP_SIDE:
            self.rect.top = sprite.rect.bottom
        if side == RIGHT_SIDE:
            self.rect.right = sprite.rect.left
        if side == BOTTOM_SIDE:
            self.rect.bottom = sprite.rect.top
        if side == LEFT_SIDE:
            self.rect.left = sprite.rect.right
    


    #############################################################################
    # Moves this object based on its speed.
    #############################################################################
    def __move(self,dx,dy):
        oldr = self.rect
        self.rect.move_ip(dx, dy)
        side = speed_to_side(dx, dy)

        for group in self.collision_groups:
            for spr in group:
                if spr.rect.colliderect(self.rect):
                    self.on_collision(side, spr, group)

        return self.rect.left-oldr.left,self.rect.top-oldr.top
    


    #############################################################################
    # Clamps this object on collision.
    #############################################################################
    def on_collision(self, side, sprite, group):
        self.clamp_off(sprite, side)
    


    #############################################################################
    # Draws this object.
    #############################################################################
    def draw(self, surf):
        surf.blit(self.image, (self.rect[0]+self.xoffset, self.rect[1]+self.yoffset))



#############################################################################
# Represents the main player.
#############################################################################
class Player(Collidable):
    


    #############################################################################
    # Initializes a new instance of Player.
    #############################################################################
    def __init__(self, top, left, events):
        Collidable.__init__(self, self.groups)
        self.images     = [loadImage("bird%d.png" % i) for i in range(1,8)]
        self.sound      = load_sound("fart.ogg")
        
        self.image      = self.images[0]
        self.rect       = self.image.get_rect()
        self.rect.top   = top
        self.rect.left  = left
        self.frame      = 0
        self.x          = 0
        self.y          = 0
        self.events     = events
    


    #############################################################################
    # Shoots a ball.
    #############################################################################
    def shoot(self):
        self.sound.play()
        PlayerDef((self.rect.centerx - 25), self.rect.centery + 12)
    


    #############################################################################
    # Updates this player's postion and appearance.
    #############################################################################
    def update(self):          
        self.frame += 1
        speed       = 10 - self.x
        self.image  = self.images[self.frame/speed%7]

        for event in self.events:
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_SPACE:
                    self.shoot()
                if event.key == pygame.K_UP:
                    self.y += -1
                if event.key == pygame.K_DOWN:
                    self.y += 1
                if event.key == pygame.K_LEFT:
                    self.x += -1
                if event.key == pygame.K_RIGHT:
                    self.x += 1
            if event.type == pygame.KEYUP:
                if event.key == pygame.K_UP:
                    self.y = 0
                if event.key == pygame.K_DOWN:
                    self.y = 0
                if event.key == pygame.K_LEFT:
                    self.x = 0
                if event.key == pygame.K_RIGHT:
                    self.x = 0
        
        if self.rect.top < 0:
            self.rect.top = 0
        if self.rect.bottom > SCREEN_HEIGHT:
            self.rect.bottom = SCREEN_HEIGHT
        if self.rect.left < 0:
            self.rect.left = 0
            
        self.move(2*self.x, 2*self.y)



#############################################################################
# Represents a player's ball.
#############################################################################
class PlayerDef(Collidable):
    


    #############################################################################
    # Initializes a new instance of PlayerDef.
    #############################################################################
    def __init__(self, x, y):
        Collidable.__init__(self, self.groups)
        self.image          = loadImage("shit.png")
        self.rect           = self.image.get_rect()
        self.rect.centery   = y
        self.rect.centerx   = x
    


    #############################################################################
    # Updates this projectile's position.
    #############################################################################     
    def update (self):
        if self.rect.top >= SCREEN_HEIGHT:
            self.kill()
        else:
            self.rect.move_ip((0, 4))



#############################################################################
# Represents a generic enemy, it will be modified according to its type.
#############################################################################
class Enemy(Collidable):
    


    #############################################################################
    # Initializes a new instance of Enemy.
    #############################################################################
    def __init__(self, pos, player, type, direction):
        Collidable.__init__(self, self.groups)
        self.image 	 = loadImage("%(type)s_%(direction)s.png" % {"type":type, "direction":direction})
        self.rect        = self.image.get_rect(topleft = pos)
        self.player 	 = player
        self.frame       = 0
        self.shotFrame 	 = -90
        self.type        = type
        self.direction 	 = direction
        self.hits        = 0
        self.blinkFrame  = 0
        self.bulletspeed = 4
        self.canshoot 	 = False
        self.sound 	 = load_sound("gunshot.ogg")
        self.canBlink    = False
    


    #############################################################################
    # Unlocks the 'blinking' sequence.
    #############################################################################
    def blink(self):
        self.canBlink = True
        self.blinkFrame = 0
    


    #############################################################################
    # Gets the player's type.
    #############################################################################
    def get_type(self):
        return self.type
    


    #############################################################################
    # Gets the number of times this player has been hit.
    #############################################################################  
    def get_hits(self):
        return self.hits
    


    #############################################################################
    # Increments this player's number of hits by 1.
    #############################################################################
    def hits_increment(self):
        self.hits += 1
    


    #############################################################################
    # Updates this player's appearance and allows it to shoot.
    #############################################################################  
    def update(self):        
        if self.canBlink:
            if self.blinkFrame < 330:
                if (self.blinkFrame % 30) == 0 and self.image.get_height() > 1:
                    self.image = pygame.Surface((1,1))
                elif (self.blinkFrame % 50) == 0 and self.image.get_height() == 1:
                    self.image = loadImage("%(type)s_%(direction)s.png" % {"type":self.type, "direction":self.direction})
            self.blinkFrame += 1
        
        self.frame += 1
        
        if (self.frame - self.shotFrame) >= 90 and self.canshoot == True:
            playerx, playery = self.player.rect.center
            weaponx, weapony = self.rect.center
            
            if self.direction == "left":
                weaponx -= 11
                weapony -= 11
            else:
                weaponx += 11
                weapony += 11
                
            dx = weaponx - playerx
            dy = weapony - playery
            
            radians = math.atan2(dy, dx)
            degrees = math.degrees(radians)
            radians = radians + math.pi

            if self.direction == "left" and degrees > 75:
                return;
            elif self.direction == "right" and degrees < 105:
                return;
            
            updatex = math.cos(radians) * self.bulletspeed
            updatey = math.sin(radians) * self.bulletspeed
            
            EnemyBullet(weaponx, weapony, updatex, updatey, self.type)
            self.sound.play()
            self.shotFrame = self.frame



#############################################################################
# Represents an enemy's bullet.
#############################################################################  
class EnemyBullet(Collidable):
    


    #############################################################################
    # Initializes a new instance of EnemyBullet.
    #############################################################################
    def __init__(self, x, y, updatex, updatey, type):
        Collidable.__init__(self, self.groups)
        self.image      = loadImage("%sbullet.png" % type)
        self.rect       = self.image.get_rect()
        self.x          = x
        self.y          = y
        self.updatex    = updatex
        self.updatey    = updatey
        self.type       = type      
    


    #############################################################################
    # Returns the enemy's type.
    ############################################################################# 
    def get_type(self):
        return self.type    
    
    


    #############################################################################
    # Updates this bullet's position by a predefined trajectory.
    #############################################################################
    def update(self):
        self.x += (self.updatex)
        self.y += (self.updatey)
        
        self.rect.centerx = self.updatex
        self.rect.centery = self.updatey
               
        self.rect.move_ip((self.x, self.y))



#############################################################################
# Represents an explosion.
#############################################################################
class Explosion(Collidable):
    


    #############################################################################
    # Initializes a new instance of Explosion.
    #############################################################################
    def __init__(self, pos, type):
        Collidable.__init__(self, self.groups)
        self.image          = loadImage("%sexplosion.png" % type)
        self.rect           = self.image.get_rect()
        self.rect.center    = pos
        self.frame          = 0
    


    #############################################################################
    # Updates this explosion's TTL.
    #############################################################################
    def update(self):
        if self.frame < 50: 
            self.frame += 1
        else:
            self.kill()
            self.frame = 0
            


#############################################################################
# Represents a solid object, such as a piece of grass or brick.
#############################################################################
class SolidObject(Collidable):
    


    #############################################################################
    # Initializes a new instance of SolidObject.
    #############################################################################
    def __init__(self, pos, type):
        Collidable.__init__(self, self.groups)
        self.image  = loadImage("%s.png" % type)
        self.rect   = self.image.get_rect(topleft = pos)



#############################################################################
# Represents a non solid object, such as a cloud.
#############################################################################
class NonSolidObject(Collidable):
    


    #############################################################################
    # Initializes a new instance of NonSolidObject.
    #############################################################################
    def __init__(self, pos, type):
        Collidable.__init__(self, self.groups)
        self.image  = loadImage("%s.png" % type)
        self.rect   = self.image.get_rect(topleft = pos)



#############################################################################
# Represents a level's finish line.
#############################################################################
class FinishLine(Collidable):
    


    #############################################################################
    # Initializes a new instance of FinishLine.
    #############################################################################
    def __init__(self, pos):
        Collidable.__init__(self, self.groups)
        self.image  = loadImage("finishline.png")
        self.rect   = self.image.get_rect(topleft = pos)



#############################################################################
# Represents a 'Live up' icon.
#############################################################################
class LiveUp(Collidable):
    


    #############################################################################
    # Initializes a new instance of LiveUp.
    #############################################################################
    def __init__(self, pos):
        Collidable.__init__(self, self.groups)
        self.image  = loadImage("bird-ups.png")
        self.rect   = self.image.get_rect(topleft = pos)